package com.priox.sigar.server.infrastructure.persistence.util;

import com.priox.sigar.server.infrastructure.persistence.exception.ApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: jmacavilca
 * Date: 07/11/12
 * Time: 03:46 PM
 * To change this template use File | Settings | File Templates.
 */
public class DateUtil {
    public static int DELTA_MILIS = 1000 * 60;

    static final Logger LOG = LoggerFactory.getLogger(DateUtil.class);

    public static String segmentoAnual(String value) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("bimestre1", "1-2");
        map.put("bimestre2", "2-4");
        map.put("bimestre3", "4-6");
        map.put("bimestre4", "6-8");
        map.put("bimestre5", "8-10");
        map.put("bimestre6", "10-12");
        map.put("trimestre1", "1-4");
        map.put("trimestre2", "4-8");
        map.put("trimestre3", "8-12");
        map.put("semestre1", "1-6");
        map.put("semestre2", "6-12");
        return map.get(value);
    }

    public static void main(String[] args) throws ParseException, InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");

        Date fechaActual = new Date();
        final boolean[] estadoInicioRetrasado = {false};
        final boolean[] estadoInicioPendiente = {false};
        final boolean[] estadoTerminarRetrasado = {false};
        final boolean[] estadoTerminarPendiente = {false};

        Runnable inicioRetrasado = builRunIniciar(estadoInicioRetrasado, new Date(fechaActual.getTime() - 1000 * 60 * 1));
        Runnable inicioPendiente = builRunIniciar(estadoInicioPendiente, new Date(fechaActual.getTime() + 1000 * 60 * 2));
        Runnable terminarRetrasado = buildTerminar(estadoTerminarRetrasado, new Date(fechaActual.getTime() + 1000 * 60 * 3));
        Runnable terminarPendiente = buildTerminar(estadoTerminarPendiente, new Date(fechaActual.getTime() - 1000 * 60 * 4));

        LinkedList<Runnable> registro = new LinkedList<Runnable>();
        registro.add(inicioPendiente);
        registro.add(inicioRetrasado);
        registro.add(terminarRetrasado);
        registro.add(terminarPendiente);


        while (!(estadoInicioRetrasado[0] && estadoInicioPendiente[0] && estadoTerminarRetrasado[0] && estadoTerminarPendiente[0])) {
            for (Runnable aRegistro : registro) {
                new Thread(aRegistro).start();
            }
            System.out.println(estadoInicioRetrasado[0] + " " + estadoInicioPendiente[0] + " " + estadoTerminarRetrasado[0] + " " + estadoTerminarPendiente[0]);
            Thread.sleep(1000 * 30);
        }
    }

    private static Runnable buildTerminar(final boolean[] result, final Date fechaInicio) {
        System.out.println("fecha registrada a terminar " + fechaInicio);
        return (new Runnable() {
            public void run() {
                try {
                    Date horaActual = new Date();
                    System.out.println("ejecutandose a las " + horaActual);
                    if (!DateUtil.inOrder(false, fechaInicio, horaActual, DateUtil.DELTA_MILIS)) {
                        result[0] = false;
                        throw new ApplicationException("La subasta debe finalizar " + FormatUtil.formatFull(fechaInicio) + ", pero actualmente es:" + FormatUtil.formatFull(horaActual));
                    }
                    result[0] = true;
                    System.out.println("terminando tarea a las " + horaActual + " porque debe terminar a " + fechaInicio);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    private static Runnable builRunIniciar(final boolean[] result, final Date fechaInicio) {
        System.out.println("fecha registrada a iniciar " + fechaInicio);
        return (new Runnable() {
            public void run() {
                try {
                    Date horaActual = new Date();
                    System.out.println("ejecutandose a las " + horaActual);
                    if (!DateUtil.inOrder(true, fechaInicio, horaActual, DateUtil.DELTA_MILIS)) {
                        result[0] = false;
                        throw new ApplicationException("La subasta debe iniciar " + FormatUtil.formatFull(fechaInicio) + ", pero actualmente es:" + FormatUtil.formatFull(horaActual));
                    }
                    result[0] = true;
                    System.out.println("iniciando tarea a las " + horaActual + " porque debio iniciar " + fechaInicio);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static boolean inOrder(Date before, Date after, long deltaMilisAllowed) {
        LOG.debug("fecha before <" + before + "> fecha after <" + after + ">");

        if (before == null || after == null) {
            return false;
        }
        if (before.after(after)) {
            System.out.println("fecha before <" + before + "> fecha after <" + after + ">");
            //tiempo en orden inverso, verificar si delta es aceptable
            long deltaMilis = Math.abs(after.getTime() - before.getTime());
            System.out.println("delta actuak <" + deltaMilis + "," + Math.asin(deltaMilis) + "> delta <" + DELTA_MILIS + ">");
            if (deltaMilis < deltaMilisAllowed) {
                System.out.println("TERMINO OK");
                return true; //delta es superior al aceptable
            }
        }
        System.out.println("no entra a considerar");

        return false;
    }

    public static boolean inOrder(boolean iniciar, Date before, Date after, long deltaMilisAllowed) {
        if (before == null || after == null) {
            return false;
        }
        if (before.after(after)) {
            //tiempo en orden inverso, verificar si delta es aceptable
            long deltaMilis = Math.abs(after.getTime() - before.getTime());
            LOG.debug("---- evaluando condicion " + before + " -> " + after + " delta actual <" + deltaMilis / 1000 + "," + Math.asin(deltaMilis) + "> delta <" + DELTA_MILIS / 1000 + ">");
            if (deltaMilis > deltaMilisAllowed) {
                return false; //delta es superior al aceptable
            }
        }
        if (iniciar) {
            LOG.debug("iniciando xq paso el limite  programado <" + before + "> -> actual <" + after + ">");
        } else {
            LOG.debug("terminando xq paso el limite programado <" + before + "> -> actual <" + after + ">");
        }
        return true;
    }

    public static Date nvl(Date date1, Date date2) {
        return date1 != null ? date1 : date2;
    }

    public static Date addDaysHoursMins(Date date, Integer dias, Integer horas, Integer minutos) {
        if (date == null) return null;
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (dias != null) cal.add(Calendar.DAY_OF_YEAR, dias);
        if (horas != null) cal.add(Calendar.HOUR_OF_DAY, horas);
        if (minutos != null) cal.add(Calendar.MINUTE, minutos);
        return cal.getTime();
    }

    public static Date min(Date date1, Date date2) {
        if (date1 == null) return date2;
        if (date2 == null) return date1;
        return date1.before(date2) ? date1 : date2;
    }

    public static Date max(Date date1, Date date2) {
        if (date1 == null) return date2;
        if (date2 == null) return date1;
        return date1.after(date2) ? date1 : date2;
    }

    public static Date notNull(Date date, String errorMsg) {
        if (date != null) return date;
        throw new IllegalArgumentException(errorMsg);
    }

    /**
     * if (  @diferencia<=30 )
     * begin
     * set @r_val=1;
     * end
     * else
     * begin
     * if ( @diferencia > 30 and @diferencia<=90 )
     * <p/>
     * set @r_val=2;
     * else
     * begin
     * if ( @diferencia > 90 and @diferencia<=180 )
     * set @r_val=3;
     * else
     * begin
     * if ( @diferencia > 180 and @diferencia<=360 )
     * set 	@r_val=4;
     * else
     * begin
     * if ( @diferencia > 361 and @diferencia<=720 )
     * set @r_val=5;
     * else
     * if ( @diferencia > 720 )
     * set  @r_val=6;
     * end
     * end
     * end
     * end
     */
    final static long MILLSECS_PER_DAY = 24 * 60 * 60 * 1000;

    public static int tipoRango(Date fechaCorte, Date fechaVencimiento) {

        long dias = (fechaCorte.getTime() - fechaVencimiento.getTime()) / MILLSECS_PER_DAY;
        if (dias <= 30)
            return 1;
        else if (dias > 30 && dias <= 60)
            return 2;
        else if (dias > 60 && dias <= 90)
            return 3;
        else if (dias > 90 && dias <= 120)
            return 4;
        else if (dias > 120 && dias <= 150)
            return 5;
        else if (dias > 150 && dias <= 180)
            return 6;
        else if (dias > 180 && dias <= 360)
            return 7;
        else
            return 8;
    }

}
